#include <list>
#include <string>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include "CIpodLog.h"
#include "CIpodEvent.h"
#include "CIpodDevice.h"
#include "CIpodIap1.h"
#include "CIpodIap2.h"
#include "CIpodConfig.h"
#include "CIpodVehicle.h"

#ifdef __ANDROID__
struct hidraw_devinfo {
    __u32 bustype;
    __s16 vendor;
    __s16 product;
};
#else
#include <linux/hidraw.h>
#endif

#define IPOD_READ_TIMEOUT           2       // 数据读线程超时时间
#define HID_HEADER_LENGTH           2       // HID信息头长度
#define USB_PACKET_MAX_LENGTH       1024    // USB每包数据最大长度
//#define IPOD_HOST_HANDLE_PATH       "/dev/gadget_transfer"
#define IPOD_HOST_HANDLE_PATH       "/dev/zjinnova_iap"

static const uint8 iAP2CheckBuf[]       = { 0xFF, 0x55, 0x02, 0x00, 0xEE, 0x10 };
static const uint8 iAP1ReqIdentifyBuf[] = { 0x55, 0x02, 0x00, 0x00, 0xFE };
static const uint8 iAP1AckBuf[]         = { 0x55, 0x04, 0x00, 0x02, 0x04, 0xEE, 0x08 };
static const uint8 iAP1NotSupportBuf[]  = { 0xFF, 0x55, 0x0E, 0x00, 0x13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEB };

BtSendData          CIpodDevice::bt_send_data = NULL;
WiFiDataCallback    CIpodDevice::wifiDataCallback = NULL;

IpodPacket::IpodPacket() {
    clear();
}

IpodPacket::IpodPacket(const IpodPacket& packet) {
    exit = packet.exit;
    len = packet.len;
    memcpy(data, packet.data, sizeof(data));
    native = packet.native;
}

IpodPacket::~IpodPacket() {

}

void IpodPacket::clear() {
    exit = false;
    len = 0;
    memset(data, 0, sizeof(data));
    native = false;
}

CIpodDevice::CIpodDevice(const char* path) {
    if (path != NULL) {
        m_Path = path;
    }
    m_Handle = 0;
    m_DeviceExist = false;
    m_ProcTid = 0;
    m_RecvTid = 0;
    m_NativeTid = 0;
    sem_init(&m_PacketSem, 0, 0);
    sem_init(&m_ProtocolSem, 0, 0);
    m_ProtInitState = IPODProtInitState::UNINIT;
    m_Iap2Support = false;
    pthread_mutex_init(&m_SendMutex, NULL);
    pthread_mutex_init(&m_PacketMutex, NULL);
    m_Protocol = NULL;
    m_Exiting = false;
}

CIpodDevice::~CIpodDevice() {
    sem_destroy(&m_PacketSem);
    sem_destroy(&m_ProtocolSem);
    pthread_mutex_destroy(&m_SendMutex);
    pthread_mutex_destroy(&m_PacketMutex);
    CIpodLog::i("CIpodDevice::~CIpodDevice successfully.\n");
}

ipod_t CIpodDevice::getHandle() {
    return m_Handle;
}

int CIpodDevice::getConnMode() {
    return m_connMode;
}

std::string CIpodDevice::getPath() {
    return m_Path;
}

int CIpodDevice::send(const uint8* data, uint16 len, bool native) {
    int ret = -1;
    int retryCount =0 ;

    if (m_Exiting) {
        return -1;
    }

    if (native) {// eap send data
        CIpodLog::i("CIpodDevice::send,EAP Send Data !\n");
        if ((ret = write(CIpodNative::getHandle(), data, len)) < 0) {
            CIpodLog::e("CIpodDevice::send, write native error!\n");
        }
        else {
            ret = 0;
        }
    }
    else if (m_connMode == ConnMode_BLUETOOTH) { //buletooth send data
        if (bt_send_data != NULL) {
            CIpodLog::e("CIpodDevice::send, bt_send_data\n");
            bt_send_data(len,data);
        }
        else {
            CIpodLog::e("CIpodDevice::send, bt_send_data is null!\n");
        }
        ret = 0;
    }
    else if (m_connMode == ConnMode_WiFi) {
        if (wifiDataCallback != NULL) {
            wifiDataCallback(len,data);
        }
        else {
            CIpodLog::e("CIpodDevice::send, wifiDataCallback is null!\n");
        }
        ret = 0;
    }
    else { //iap2 send data
        if (CIpodConfig::getMode() == Mode_MEDIA) {
            if (m_Handle <= 0) {
                return -1;
            }
            pthread_mutex_lock(&m_SendMutex);
        reSendData :
            uint16 validDataLen = USB_PACKET_MAX_LENGTH - HID_HEADER_LENGTH;
            uint16 count = len / validDataLen + ((len % validDataLen) == 0 ? 0 : 1);
            uint8 buf[USB_PACKET_MAX_LENGTH];
            if (count == 1) {
                buf[0] = 0x01;
                buf[1] = 0x00;
                memcpy(buf + HID_HEADER_LENGTH, data, len);
                ret = write(m_Handle, buf, len + HID_HEADER_LENGTH);
            }
            else {
                uint16 alreadyWriteLen = 0;
                for (uint16 i = 0; i < count; i++) {
                    buf[0] = 0x01;
                    if (i == 0) {
                        buf[1] = 0x02;
                    }
                    else if (i == count -1) {
                        buf[1] = 0x01;
                    }
                    else {
                        buf[1] = 0x03;
                    }
                    if (i == count - 1) {
                        memcpy(buf + HID_HEADER_LENGTH, data + alreadyWriteLen, len - validDataLen * (count - 1));
                        ret = write(m_Handle, buf, len - validDataLen * (count - 1) + HID_HEADER_LENGTH);
                        alreadyWriteLen += (len - validDataLen * (count - 1));
                    }
                    else {
                        memcpy(buf + HID_HEADER_LENGTH, data + alreadyWriteLen, validDataLen);
                        ret = write(m_Handle, buf, USB_PACKET_MAX_LENGTH);
                        alreadyWriteLen += validDataLen;
                    }
                }
            }
            if (ret < 0) {
                CIpodLog::w("CIpodDevice::send, send error in device mode (%d)--%s\n", errno, strerror(errno));
                usleep(200000);
                retryCount ++;
                CIpodLog::e("CIpodDevice::send failed, retryCount:%d m_DeviceExist:%d m_Exiting:%d!\n",retryCount,m_DeviceExist,
                            m_Exiting);
                if (retryCount <3 && m_DeviceExist) {
                    CIpodLog::w("CIpodDevice::send, send error in device mode (%d)--%s --- retryCount:(%d)\n", errno, strerror(errno),
                                retryCount);
                    goto reSendData;
                }
//                CIpodEvent::sendEvent(IPOD_EVT_CONNECT_STATUS_CHANGED, m_Handle, 0);
                ret = -1;
            }
            else {
//                debugHexBuf("write", data, len);
                ret = 0;
            }
            pthread_mutex_unlock(&m_SendMutex);
        }
        else if (CIpodConfig::getMode() == Mode_CARLIFE
                 || CIpodConfig::getMode() == Mode_CARPLAY) {
            if (m_Handle <= 0) {
                return -1;
            }
            pthread_mutex_lock(&m_SendMutex);
//            debugHexBuf("IAP2-USB-Send", data, len);
            ret = write(m_Handle, data, len);
            if (ret < 0) {
                CIpodLog::w("write gadget_transfer error. errno: %d, errorstr: %s\n", errno, strerror(errno));
                ret = 0;
            }
            else {
                ret = 0;
            }
            pthread_mutex_unlock(&m_SendMutex);
        }
    }
    return ret;
}

bool CIpodDevice::isDeviceExist() {
    return m_DeviceExist;
}

bool CIpodDevice::isExiting() {
    return m_Exiting;
}

int CIpodDevice::create() {
    CIpodLog::i("create iAP device and iAP2 support: %d.\n", m_Iap2Support);
    int ret = -1;
    if ((ret = openDevice()) == -1) {
        CIpodLog::e("CIpodDevice::create, device open failed!\n");
        return ret;
    }
    if (CIpodConfig::getIdentification()->eaprotocol.nativeid > 0 && CIpodConfig::getMode() == Mode_CARLIFE) {
        if ((ret = CIpodNative::init()) != 0) {
            CIpodLog::e("CIpodDevice::create, native device open failed!\n");
            return ret;
        }
        else {
            if ((ret = pthread_create(&m_NativeTid, NULL, nativeThread, (void*)this)) != 0) {
                CIpodLog::e("CIpodDevice::create, native thread create failed!\n");
                return ret;
            }
            CIpodNative::registerSigioHandle();
        }
    }
    if ((ret = pthread_create(&m_ProcTid, NULL, procThread, (void*)this)) != 0) {
        CIpodLog::e("CIpodDevice::create, proc thread create failed!\n");
        return ret;
    }


    if (m_connMode == IPOD_CONN_TYPE_USB_HOST || m_connMode == IPOD_CONN_TYPE_USB_DEVICE) {
        if ((ret = pthread_create(&m_RecvTid, NULL, recvThread, (void*)this)) != 0) {
            CIpodLog::e("CIpodDevice::create, recv thread create failed!\n");
            return ret;
        }
    }
    m_DeviceExist = true;

    return 0;
}

int CIpodDevice::connectIap2() {
    CIpodLog::i("iAP2 Link Initialization start.\n");
    int ret = -1;

    if (CIpodConfig::getProtocol() == IPODProtocol::IAP1) {
        m_Protocol = new CIpodIap1();
        m_ProtInitState = IPODProtInitState::SUCCEED;
    }
    else {
        if (checkIap2Support() != -1) {
            if (m_Iap2Support) {
                CIpodLog::i("iAP2 Link CheckIap22Support success.\n");
                m_Protocol = new CIpodIap2();
            }
            else {
                CIpodLog::e("iAP2 Link Initialization Detect, but not support iAP2.\n");
                if (CIpodConfig::getProtocol() == IPODProtocol::IAP2) {
                    send(iAP1NotSupportBuf, sizeof(iAP1NotSupportBuf));
                }
                else if (CIpodConfig::getProtocol() == IPODProtocol::ALL) {
                    m_Protocol = new CIpodIap1();
                }
            }
        }
    }
    if (m_Protocol != NULL) {
        CIpodLog::i("iAP2 Link m_Protocol != NULL. \n");
        m_Protocol->registerProxy(this);
        ret = m_Protocol->init(m_connMode);
    }
    else {
        CIpodLog::e("CIpodDevice::create, ipod protocol is null!\n");
        ret = -1;
    }

    if (ret == 0) {
        CIpodEvent::sendEvent(IPOD_EVT_CONNECT_STATUS_CHANGED, m_Handle, 1);
//      if (m_Protocol != NULL) {
//          m_Protocol->getDeviceInformation();
//      }
    }
    return ret;
}

int CIpodDevice::destroy() {
    CIpodLog::i("CIpodDevice::destroy start handle: %d\n", m_Handle);

    int ret = -1;
    if (m_connMode == ConnMode_USB_HOST || m_connMode == ConnMode_USB_DEVICE) {
        if (m_Protocol != NULL) {
            ret = m_Protocol->deinit();
        }
    }

    m_Exiting = true;

    if (m_Handle > 0) {
        close(m_Handle);
        m_Handle = 0;
    }
    CIpodLog::i("CIpodDevice::destroy exit handle successfully.\n");

    if (m_connMode == ConnMode_BLUETOOTH) {
        CIpodVehicle::setbtSppConnStatus(ipod_SppState_ReadyNoInitialized);
    }
    else {
        CIpodVehicle::setWiFiConnStatus(WIFI_NOCONNECT);
    }

    CIpodLog::i("CIpodDevice::destroy, ProcTid = %lu \n", m_ProcTid);
//    if (m_ProcTid > 0) {
//        IpodPacket *packet = new IpodPacket();
//        if (NULL != packet) {
//            packet->exit = true;
//            packetAdd(packet);
//            delete packet;
//        }

//        if (m_ProcTid > 0) {
//            CIpodLog::i("CIpodDevice::destroy, pthread_join \n");
//            // 等待子线程退出
//            pthread_join(m_ProcTid, NULL);
//            //发送一次信号，让子线程执行并退出
//            sem_post(&m_PacketSem);
//        }
//        // 等待子线程退出
//        CIpodLog::i("CIpodDevice::destroy, pthread_join m_ProcTimd = %lu \n", m_ProcTid);
//        pthread_join(m_ProcTid, NULL);
//        //发送一次信号，让子线程执行并退出
//        CIpodLog::i("CIpodDevice::destroy, sending sem_post. and ProcTid = %lu \n", m_ProcTid);
//        sem_post(&m_PacketSem);
//        CIpodLog::i("CIpodDevice::destroy, sended sem_post. and ProcTid = %lu \n", m_ProcTid);
//
//        // proc 子线程安全退出了 重置 ProcTid
//        m_ProcTid = 0;
//        CIpodLog::i("CIpodDevice::destroy, set m_ProcTid = 0 \n");
// TODO 还需要清空 m_Packets 里面的数据
//        usleep(2000);
//        CIpodLog::i("CIpodDevice::destroy, exit pthread_join m_ProcTid \n");
//        if(m_Packets.empty()){
//            CIpodLog::i("CIpodDevice::destroy, m_Packets is empty ...\n");
//        } else {
//            for (std::list<IpodPacket *>::iterator iter = m_Packets.begin(); iter != m_Packets.end(); ++iter) {
//                IpodPacket *packet = *iter;
//                if (NULL != packet) {
//                    delete packet;
//                }
//                iter = m_Packets.erase(iter);
//                //++iter;
//            }
//            if(m_Packets.empty()){
//                CIpodLog::i("CIpodDevice::destroy, erase m_Packets is empty ...\n");
//            }
//      }
//    }

    if (m_ProcTid > 0) {
        pthread_join(m_ProcTid, NULL);
        sem_post(&m_PacketSem);
        m_ProcTid = 0;
    }

    CIpodLog::i("iAP2 ProcessThread has already terminated.\n");

    if (m_RecvTid > 0) {
        pthread_join(m_RecvTid, NULL);
        m_RecvTid = 0;
    }
    CIpodLog::i("iAP2 ReceiveThread has already terminated.\n");

    if (m_NativeTid > 0) {
        pthread_join(m_NativeTid, NULL);
        m_NativeTid = 0;
    }
    CIpodLog::i("iAP2 NativeThread has already terminated.\n");

    usleep(2000);

    if (m_Protocol != NULL) {
        // ret = m_Protocol->deinit();
        m_Protocol->unregisterProxy();
        delete m_Protocol;
        m_Protocol = NULL;
    }

    CIpodLog::i("CIpodDevice::destroy successfully.\n");
    return ret;
}

int CIpodDevice::notifyBtStatus(bool connected) {
    if (m_Protocol != NULL) {
        return m_Protocol->notifyBtStatus(connected);
    }
    return -1;
}

int CIpodDevice::notifyUSBAudioStatus(bool enabled) {
    if (m_Protocol != NULL) {
        return m_Protocol->notifyUSBAudioStatus(enabled);
    }
    return -1;
}

int CIpodDevice::sendMediaCommand(int command) {
    if (m_Protocol != NULL) {
        return m_Protocol->sendMediaCommand(command);
    }
    return -1;
}

int CIpodDevice::sendMediaCommandDown(int command) {
    if (m_Protocol != NULL) {
        return m_Protocol->sendMediaCommandDown(command);
    }
    return -1;
}

int CIpodDevice::sendMediaCommandUp(int command) {
    if (m_Protocol != NULL) {
        return m_Protocol->sendMediaCommandUp(command);
    }
    return -1;
}

int CIpodDevice::setPlayingTrackPosition(uint32 position) {
    if (m_Protocol != NULL) {
        return m_Protocol->setPlayingTrackPosition(position);
    }
    return -1;
}

int CIpodDevice::getShuffleMode(int* mode) {
    if (m_Protocol != NULL) {
        return m_Protocol->getShuffleMode(mode);
    }
    return -1;
}

int CIpodDevice::getRepeatMode(int* mode) {
    if (m_Protocol != NULL) {
        return m_Protocol->getRepeatMode(mode);
    }
    return -1;
}

int CIpodDevice::getPlayingTrackDuration(int* duration) {
    if (m_Protocol != NULL) {
        return m_Protocol->getPlayingTrackDuration(duration);
    }
    return -1;
}

int CIpodDevice::getPlayingTrackTitle(std::string& title) {
    if (m_Protocol != NULL) {
        return m_Protocol->getPlayingTrackTitle(title);
    }
    return -1;
}

int CIpodDevice::getPlayingTrackAlbum(std::string& album) {
    if (m_Protocol != NULL) {
        return m_Protocol->getPlayingTrackAlbum(album);
    }
    return -1;
}

int CIpodDevice::getPlayingTrackArtist(std::string& artist) {
    if (m_Protocol != NULL) {
        return m_Protocol->getPlayingTrackArtist(artist);
    }
    return -1;
}

int CIpodDevice::getPlayingTrackAlbumArtwork() {
    if (m_Protocol != NULL) {
        return m_Protocol->getPlayingTrackAlbumArtwork();
    }
    return -1;
}

int CIpodDevice::getPlayStatus(IPODPlayStatus* status) {
    if (m_Protocol != NULL) {
        return m_Protocol->getPlayStatus(status);
    }
    return -1;
}

int CIpodDevice::getPlayingTrackInfo(int32* index, uint32* count) {
    if (m_Protocol != NULL) {
        return m_Protocol->getPlayingTrackInfo(index, count);
    }
    return -1;
}

int CIpodDevice::getPlayingChapterInfo(int32* index, uint32* count) {
    if (m_Protocol != NULL) {
        return m_Protocol->getPlayingChapterInfo(index, count);
    }
    return -1;
}

int CIpodDevice::isListLevelSupported(bool* supported) {
    if (m_Protocol != NULL) {
        return m_Protocol->isListLevelSupported(supported);
    }
    return -1;
}

int CIpodDevice::resetMusicList(uint32* num) {
    if (m_Protocol != NULL) {
        return m_Protocol->resetMusicList(num);
    }
    return -1;
}

int CIpodDevice::selectMusicList(int32 index, int32 level, uint32* num) {
    if (m_Protocol != NULL) {
        return m_Protocol->selectMusicList(index, level, num);
    }
    return -1;
}

int CIpodDevice::getMusicList(uint32 startIndex, uint32 readCount, IPODMusicList* musicList) {
    if (m_Protocol != NULL) {
        return m_Protocol->getMusicList(startIndex, readCount, musicList);
    }
    return -1;
}

int CIpodDevice::getMusicList_c(uint32 startIndex, uint32 readCount, C_IPODMusicList* musicList) {
    if (m_Protocol != NULL) {
        return m_Protocol->getMusicList_c(startIndex, readCount, musicList);
    }
    return -1;
}

int CIpodDevice::selectMusicPlayingList(int32 index) {
    if (m_Protocol != NULL) {
        return m_Protocol->selectMusicPlayingList(index);
    }
    return -1;
}

int CIpodDevice::getMusicPlayingList(uint32 startIndex, uint32 readCount, C_IPODMusicList* musicList) {
    if (m_Protocol != NULL) {
        return m_Protocol->getMusicPlayingList(startIndex, readCount, musicList);
    }
    return -1;
}

int CIpodDevice::launchApp(const char* appBundleID) {
    if (m_Protocol != NULL) {
        return m_Protocol->launchApp(appBundleID);
    }
    return -1;
}

int CIpodDevice::setLocationInformation(const char* sentence) {
    if (m_Protocol != NULL) {
        return m_Protocol->setLocationInformation(sentence);
    }
    return -1;
}

int CIpodDevice::iap2CarPlayStartSession(int type) {
    if (m_Protocol != NULL) {
        return m_Protocol->carPlayStartSession(type);
    }
    return -1;
}

int CIpodDevice::ipod_BT_spp_data_pro(int cDataLen, const char* pData) {
    if (m_Exiting) {
        return 0;
    }

    if (!m_Iap2Support) {
        if (memcmp(iAP2CheckBuf, pData, sizeof(iAP2CheckBuf)) == 0) {
            CIpodLog::i("[  Device   ] step 2:  DETECT over Rfcomm.\n");
            m_ProtInitState = IPODProtInitState::SUCCEED;
            m_Iap2Support = true;
            sem_post(&m_ProtocolSem);
            return 0;
        }
    }

    uint16 length1 = IAP2_BIG_ENDIAN(pData[2], pData[3]);
    uint16 length2 = 0;
    uint16 length3 = 0;

    // CIpodLog::w( "Rfcomm data ( Device -> Accessory ) size: %d. First iAP2 Packet size: %d.\n", cDataLen, length1 );

    // only one iAP2 Packet in Rfcomm data, analyse first iAP2 Packet.

    IpodPacket* packet = new IpodPacket();

    packet->len = length1;
    packet->native = false;
    memcpy(packet->data, pData, length1);
    packetAdd(packet);

    // more than one iAP2 Packet in Rfcomm data, just anlayse second iAP2 Packet( ugly ).

    if (cDataLen > length1) {
        length2 = IAP2_BIG_ENDIAN(pData[length1 + 2], pData[length1 + 3]);

        CIpodLog::w("Rfcomm data ( Device -> Accessory ) size: %d, First iAP2 Packet size: %d, Second iAP2 Packet size: %d\n",
                    cDataLen, length1, length2);

        IpodPacket* packet = new IpodPacket();

        packet->len = length2;
        packet->native = false;
        memcpy(packet->data, pData + length1, length2);
        packetAdd(packet);
    }

    // the third iAP2 Packet

    if (cDataLen > length1 + length2) {
        length3 = IAP2_BIG_ENDIAN(pData[ length1 + length2 + 2 ], pData[ length1 + length2 + 3 ]);

        CIpodLog::w("Rfcomm data ( Device -> Accessory ) size: %d, First iAP2 Packet size: %d, Second iAP2 Packet size: %d, Third iAP2 Packet size: %d\n",
                    cDataLen, length1, length2, length3);

        IpodPacket* packet = new IpodPacket();

        packet->len = length3;
        packet->native = false;
        memcpy(packet->data, pData + length1 + length2, length3);
        packetAdd(packet);
    }

    return 0;
}

int CIpodDevice::sendWiFiData(int cDataLen, const char* pData) {
    if (m_Exiting) {
        return 0;
    }

    if (!m_Iap2Support) {
        if (memcmp(iAP2CheckBuf, pData, sizeof(iAP2CheckBuf)) == 0) {
            CIpodLog::i("[  Device   ] step 2:  DETECT over Wi-Fi.\n");
            m_ProtInitState = IPODProtInitState::SUCCEED;
            m_Iap2Support = true;
            sem_post(&m_ProtocolSem);
            return 0;
        }
    }
    IpodPacket* packet = new IpodPacket();

    packet->len = cDataLen;
    packet->native = false;
    memcpy(packet->data, pData, cDataLen);
    packetAdd(packet);

    return 0;
}

void CIpodDevice::ipodRegistBtSppSendData(BtSendData n_bt_spp_send_data) {
    if (n_bt_spp_send_data != NULL) {
        CIpodDevice::bt_send_data = n_bt_spp_send_data;
    }
}

void CIpodDevice::setWiFiDataCallback(WiFiDataCallback callback) {
    if (callback != NULL) {
        CIpodDevice::wifiDataCallback = callback;
    }
}

int CIpodDevice::iap2BluetoothPairingAccessoryInformation(const char* inPairingDataP192,const char* inPairingDataP256) {
    if (m_Protocol != NULL) {
        return m_Protocol->bluetoothPairingAccessoryInformation(inPairingDataP192,inPairingDataP256);
    }
    return -1;
}

int CIpodDevice::iap2BluetoothPairingStatus(int success, const char* cancelReason) {
    if (m_Protocol != NULL) {
        return m_Protocol->bluetoothPairingStatus(success,cancelReason);
    }
    return -1;
}

int CIpodDevice::iap2OOBBTPairingCompletionInformation(int resultCode) {
    if (m_Protocol != NULL) {
        return m_Protocol->oOBBTPairingCompletionInformation(resultCode);
    }
    return -1;
}

int CIpodDevice::sendEAData(const uint8* data, uint16 len) {
    if (m_Protocol != NULL) {
        return m_Protocol->sendEAData(data, len);
    }
    return -1;
}

int CIpodDevice::getEANativeState() {
    return CIpodNative::getNativeState();
}

int CIpodDevice::openDevice() {
    int ret = -1;
    int handle = 0;
    if ((CIpodConfig::getMode() == Mode_MEDIA) ||
            (CIpodConfig::getMode() == Mode_CARPLAY && CIpodConfig::connMode() == ConnMode_USB_DEVICE)) {
        m_connMode = ConnMode_USB_DEVICE;
        std::list<std::string> devices;
        bool isEmpty = m_Path.empty();
        CIpodLog::i("CIpodDevice::openDevice --- isEmpty == %d", isEmpty);
        if (isEmpty) {
            int num = 0;
            FILE* fstream = NULL;
            char buff[1024] = { 0 };
            if (NULL == (fstream = popen("ls /dev/hidraw* |grep -c 'hidraw'", "r"))) {
                CIpodLog::e("CIpodDevice::openDevice, execute command failed: %s", strerror(errno));
                return -1;
            }
            if (NULL == fgets(buff, sizeof(buff), fstream)) {
                CIpodLog::e("CIpodDevice::openDevice, execute command buf is null\n");
                pclose(fstream);
                return -1;
            }
            pclose(fstream);
            if (buff[0] <= '9' && buff[0] >= '0') {
                num = buff[0] - '0';
            }
            for (int i = 0; i < num; i++) {
                char str[128] = { 0 };
                sprintf(str, "/dev/hidraw%d", i);
                devices.push_back(str);
            }
        }
        else {
            devices.push_back(m_Path);
        }

        for (std::list<std::string>::iterator iter = devices.begin(); iter != devices.end(); ++iter) {
            if ((handle = open(iter->data(), O_RDWR)) > 0) {
                if (isIpodDevice(handle)) {
                    ret = 0;
                    break;
                }
                else {
                    close(handle);
                }
            }
        }
    }
    else if ((CIpodConfig::getMode() == Mode_CARLIFE) || (CIpodConfig::getMode() == Mode_CARPLAY
                                                          && CIpodConfig::connMode() == ConnMode_USB_HOST)) {
        if ((handle = open(IPOD_HOST_HANDLE_PATH, O_RDWR)) > 0) {
            CIpodLog::i("open iAP2 over HostUsb device success.\n");
            m_connMode = ConnMode_USB_HOST;
            ret = 0;
        }
        else {
            CIpodLog::i("ipod usb host device open failed!\n");
            ret = -1;
        }
    }
    else if (CIpodConfig::getMode() == Mode_CARPLAY && CIpodConfig::connMode() == ConnMode_BLUETOOTH) {
        CIpodLog::i("iAP2 over bluetooth for wireless CarPlay.\n");
        if (CIpodVehicle::getbtSppConnStatus() == ipod_SppState_Connected) {
            handle = 0xFF;
            m_connMode = ConnMode_BLUETOOTH;
            ret = 0;
        }
        else {
            CIpodLog::i("ipod over buletooth device open failed!\n");
            ret = -1;
        }
    }
    else if (CIpodConfig::getMode() == Mode_CARPLAY && CIpodConfig::connMode() == ConnMode_WiFi) {
        CIpodVehicle::setWiFiConnStatus(WIFI_CONNECTED);
        handle = 0xFE;
        m_connMode = ConnMode_WiFi;
        ret = 0;
        CIpodLog::i("iAP2 over Wi-Fi for wireless CarPlay.\n");
    }

    if (ret == 0) {
        m_Handle = handle;
    }
    return ret;
}

bool CIpodDevice::isIpodDevice(int handle) {
    struct hidraw_devinfo dinfo;
#ifdef __ANDROID__
    ioctl(handle, _IOR('H', 0x03, struct hidraw_devinfo), &dinfo);
#else
    ioctl(handle, HIDIOCGRAWINFO, &dinfo);
#endif
    return (dinfo.vendor == 0x05AC) ? true : false;
}

int CIpodDevice::checkIap2Support() {
    CIpodLog::i("[ Accessory ] step 1:  DETECT\n");

    m_ProtInitState = IPODProtInitState::INITING;
    if (send(iAP2CheckBuf, sizeof(iAP2CheckBuf)) == -1) {
        CIpodLog::i("CIpodDevice::checkIap2Support, send error! %s\n", strerror(errno));
        return -1;
    }

    sem_wait(&m_ProtocolSem);
    return (m_ProtInitState == IPODProtInitState::SUCCEED) ? 0 : -1;
}

int CIpodDevice::packetProc(const uint8* data, uint16 len, bool native) {
    if (m_Protocol != NULL) {
        return m_Protocol->packetProc(data, len, native);
    }
    return -1;
}

void CIpodDevice::packetAdd(IpodPacket* packet) {
    if (packet == NULL || packet == nullptr || m_Exiting) {
        return;
    }
    pthread_mutex_lock(&m_PacketMutex);
    m_Packets.push_back(packet);
    pthread_mutex_unlock(&m_PacketMutex);
    sem_post(&m_PacketSem);
}

void CIpodDevice::debugHexBuf(const char* tag, const uint8* data, uint16 len) {
    uint16 debugLen = len;
    std::string buf;
    buf.append(tag);
    buf.append(" ---- ");
    for (uint16 i = 0; i < debugLen; i++) {
        char temp[10];
        sprintf(temp, "%02x ", data[i]);
        buf.append(temp);
    }
    buf.append("\n");
    CIpodLog::d("%s", buf.data());
}

void CIpodDevice::debugHexBuf1(const char* tag, const char* data, uint16 len) {
    uint16 debugLen = (len > 32) ? 32 : len;

#ifdef __ANDROID__
    std::string buf;
    buf.append(tag);
    buf.append(" ---- ");
    for (uint16 i = 0; i < debugLen; i++) {
        char temp[10];
        sprintf(temp, "%02x ", data[i]);
        buf.append(temp);
    }
    buf.append("\n");
    CIpodLog::d("%s", buf.data());
#else
    CIpodLog::d("%s ---- ", tag);
    for (uint16 i = 0; i < debugLen; i++) {
        CIpodLog::d("%02x ", data[i]);
    }
    CIpodLog::d("\n");
#endif
}

void* CIpodDevice::procThread(void* arg) {
    CIpodDevice* device = (CIpodDevice*)arg;

    int handle = device->getHandle();

    CIpodLog::w("==================== ProcessThread Start fd: %d, pthread_self() = %lu ====================\n", handle,
                pthread_self());
    while (1) {
        sem_wait(&device->m_PacketSem);

        if (device->m_Exiting) {
            CIpodLog::i("ProcessThread exiting because iAP2 Device has been exited.\n");
            break;
        }
        pthread_mutex_lock(&device->m_PacketMutex);
        if (device->m_Packets.empty() || device->m_Packets.size() == 0) {
            CIpodLog::i("CIpodDevice::procThread, m_Packets is empty! continue...\n");
            pthread_mutex_unlock(&device->m_PacketMutex);
            continue;
        }

        std::list<IpodPacket*>::iterator iter = device->m_Packets.begin();
        if (iter != device->m_Packets.end()) {
            IpodPacket* packet = *iter;
            if (packet == NULL || packet->exit || device->m_Exiting) {
                CIpodLog::i("packet->exit = %d, device->m_Exiting = %d\n", packet->exit, device->m_Exiting);
                device->m_Packets.erase(iter);
                if (NULL != packet) {
                    delete packet;
                }
                pthread_mutex_unlock(&device->m_PacketMutex);
                break;
            }

            if (NULL != packet && device->m_Exiting == false) {
                device->packetProc(packet->data, packet->len, packet->native);
            }

            usleep(1000);
            CIpodLog::i("packet->exit = %d, device->m_Exiting = %d\n", packet->exit, device->m_Exiting);
            if (device->m_Exiting) {

                pthread_mutex_unlock(&device->m_PacketMutex);
                break;
            }

            device->m_Packets.erase(iter);
            if (NULL != packet) {
                delete packet;
            }
        }
        usleep(1000);
        pthread_mutex_unlock(&device->m_PacketMutex);
    }
    device->m_ProcTid = 0;
    CIpodLog::w("==================== ProcessThread end fd: %d   ====================\n", handle);
    return NULL;
}

void* CIpodDevice:: recvThread(void* arg) {
    int ret = -1;
    fd_set fds;
    struct timeval timeout;
    timeout.tv_sec = IPOD_READ_TIMEOUT;
    timeout.tv_usec = 0;
    ipod_t fd = 0;

    uint8 hidReceiveBuf[IPOD_PACKET_MAX_LENGTH] = { 0 };

    IpodPacket packet;
    packet.native = false;
    CIpodDevice* device = (CIpodDevice*) arg;
    if (device != NULL) {
        fd = device->getHandle();
    }
    else {
        CIpodLog::i("CIpodDevice::recvThread, device is null !!!!!!!!!!!!!!!\n");
        device->m_RecvTid = 0;
        return NULL;
    }
    CIpodLog::i("==================== ReceiveThread start fd: %d ====================\n", fd);
    while (1) {
        if (device->m_Exiting) {
            CIpodLog::w("ReceiveThread exiting because iAP2 Device has been exited.\n");
            break;
        }

        timeout.tv_sec = IPOD_READ_TIMEOUT;
        FD_ZERO(&fds);
        FD_SET(fd, &fds);

        ret = select(fd + 1, &fds, NULL, NULL, &timeout);
        if (ret < 0) {
            CIpodLog::i("iAP2 ReceiveThread select error\n");
            break;
        }
        else if (ret == 0) {
            CIpodLog::i("iAP2 ReceiveThread select timeout(no message), continue select.\n");
        }
        else if (ret > 0 && FD_ISSET(fd, &fds)) {
            // 给 errno 复位,防止是上一次操作的错误
            errno = 0; //
            ret = read(fd,(uint8*) hidReceiveBuf, IPOD_PACKET_MAX_LENGTH);
//            device->debugHexBuf("IAP2-USB-Recv", hidReceiveBuf, IPOD_PACKET_MAX_LENGTH);
            if (ret < 0) {
                CIpodLog::e("iAP2 ReceiveThread read error. errno is %d, strerror is %s, ret = %d, IPOD_PACKET_MAX_LENGTH = %d\n", errno, strerror(errno), ret, IPOD_PACKET_MAX_LENGTH);
                break;
            }
            else if (ret > 0) {
                int receivedPacketLen = ret;
                int hidHeaderLen = 0;
                if (CIpodConfig::getMode() == Mode_MEDIA) {
                    hidHeaderLen = HID_HEADER_LENGTH;
                    if (hidReceiveBuf[1] == 0x00 || hidReceiveBuf[1] == 0x01) {
                        memcpy(packet.data + packet.len, hidReceiveBuf + hidHeaderLen, receivedPacketLen - hidHeaderLen);
                        packet.len += (receivedPacketLen - hidHeaderLen);
                    }
                    else {
                        memcpy(packet.data + packet.len, hidReceiveBuf + hidHeaderLen, receivedPacketLen - hidHeaderLen);
                        packet.len += (receivedPacketLen - hidHeaderLen);
                        continue;
                    }
                }
                else if (CIpodConfig::getMode() == Mode_CARLIFE
                         || CIpodConfig::getMode() == Mode_CARPLAY) {
                    hidHeaderLen = 0;
                    memcpy(packet.data + packet.len, hidReceiveBuf + hidHeaderLen, receivedPacketLen - hidHeaderLen);
                    packet.len += (receivedPacketLen - hidHeaderLen);
                }

                if (device->m_ProtInitState == IPODProtInitState::INITING) {
                    if (memcmp(iAP2CheckBuf, packet.data, sizeof(iAP2CheckBuf)) == 0) {
                        CIpodLog::i("[  Device   ] step 2:  DETECT over USB.\n");
                        device->m_ProtInitState = IPODProtInitState::SUCCEED;
                        device->m_Iap2Support = true;
                        sem_post(&device->m_ProtocolSem);
                    }
                    else if (memcmp(iAP1ReqIdentifyBuf, packet.data, sizeof(iAP1ReqIdentifyBuf)) == 0
                             || memcmp(iAP1AckBuf, packet.data, sizeof(iAP1AckBuf)) == 0) {
                        CIpodLog::i("CIpodDevice::recvThread, support iap1!\n");
                        device->m_ProtInitState = IPODProtInitState::SUCCEED;
                        device->m_Iap2Support = false;
                        sem_post(&device->m_ProtocolSem);
                    }
                }
                else {
                    device->packetAdd(new IpodPacket(packet));
                }
                packet.clear();
            }
        }
    }
    if (device->m_ProtInitState == IPODProtInitState::INITING) {
        device->m_ProtInitState = IPODProtInitState::FAILED;
        sem_post(&device->m_ProtocolSem);
    }
    device->m_DeviceExist = false;
    device->m_RecvTid = 0;
    CIpodEvent::sendEvent(IPOD_EVT_CONNECT_STATUS_CHANGED, fd, 0);
    CIpodLog::w("==================== ReceiveThread end fd: %d   ====================\n", fd);
    return NULL;
}

void* CIpodDevice::nativeThread(void* arg) {
    int ret = -1;
    fd_set fds;
    struct timeval timeout;
    timeout.tv_sec = 5;
    timeout.tv_usec = 0;
    int nativefd = 0;

    CIpodDevice* device = (CIpodDevice*) arg;
    nativefd = CIpodNative::getHandle();
    CIpodLog::i("CIpodDevice::nativeThrad, start ~~~~~~~~~~~~~~~~~~~~~~~~~ nativefd = %d\n", nativefd);
    while (1) {
        if (device->m_Exiting) {
            CIpodLog::i("CIpodDevice::nativeThread, ipod is destroyed, exiting...!\n");
            break;
        }

        timeout.tv_sec = 5;
        FD_ZERO(&fds);
        FD_SET(nativefd, &fds);
        ret = select(nativefd + 1, &fds, NULL, NULL, &timeout);
        if (ret < 0) {
            CIpodLog::e("CIpodDevice::nativeThrad, select error!!!!!!!!!!!!!!!\n");
            break;
        }
        else if (ret == 0) {
            CIpodLog::i("CIpodDevice::nativeThrad, select timeout(no message)--continue select!!!!!!!!!\n");
        }
        else if (ret > 0 && FD_ISSET(nativefd, &fds)) {
            IpodPacket* packet = new IpodPacket();
            ret = read(nativefd, (uint8*) packet->data, 4096);
            if (ret < 0) {
                CIpodLog::i("CIpodDevice::nativeThrad, read return(%d)--%s!!!!!!!!!\n", errno, strerror(errno));
                if (NULL != packet) {
                    delete packet;
                }
                break;
            }
            else if (ret > 0) {
                packet->len = ret;
                packet->native = true;
                device->packetAdd(packet);
            }
        }
    }
    device->m_NativeTid = 0;
    CIpodLog::i("CIpodDevice::nativeThrad, exit success!!!!\n");
    return 0;
}
